#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/epoll.h>


char rot13_char(char c) { 
	if ((c >= 'a' && c <= 'm') || (c >= 'A' && c <= 'M')) 
		return c + 13; 
	else if ((c >= 'n' && c <= 'z') || (c >= 'N' && c <= 'Z')) 

		return c - 13; 
	else return c;
}

#define MAXEVENTS 256

int main()
{
	int listen_fd, socket_fd;
	int n, i; int efd; 
	struct epoll_event event, *events;

	socklen_t cli_len;
	struct sockaddr_in serv_addr, cli_addr;
	listen_fd = socket(AF_INET, SOCK_STREAM, 0);

	fcntl(listen_fd, F_SETFL, O_NONBLOCK);
	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(15555);

	int opt = 1;
    setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	bind(listen_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
	listen(listen_fd, 1024);

	// signal(SIGPIPE, SIG_IGN);
	efd = epoll_create1(0);

	if (efd <= 0 ) {
		fprintf(stdout, "epoll create fail\n");
	}

	event.data.fd = listen_fd;
	event.events = EPOLLIN | EPOLLET;
	if (epoll_ctl(efd, EPOLL_CTL_ADD, listen_fd, &event) == -1) {
		fprintf(stdout, "epoll control fail\n");		
	}

	events = (struct epoll_event *) calloc(MAXEVENTS, sizeof(event));

	while(1) {
		n = epoll_wait(efd, events, MAXEVENTS, -1);
		fprintf(stdout, "epoll_wait wakeup\n");		
		for (i = 0; i < n; ++i) {
			if ((events[i].events & EPOLLERR)
				|| (events[i].events & EPOLLHUP)
				|| !(events[i].events & EPOLLIN)) {
				fprintf(stderr, "epoll error\n");
				close(events[i].data.fd);
				continue;
			} else if (listen_fd == events[i].data.fd) {
				struct sockaddr_storage ss;
				socklen_t slen = sizeof(ss);

				int conn_fd = accept(listen_fd, (struct sockaddr *) &ss, &slen);

				if (conn_fd <= 0) {
					fprintf(stderr, "accept failed\n");
				} else {
					fcntl(conn_fd, F_SETFL, O_NONBLOCK);
				    // setsockopt(conn_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
				    event.data.fd = conn_fd;
				    event.events = EPOLLIN | EPOLLET;
				    if (epoll_ctl(efd, EPOLL_CTL_ADD, conn_fd, &event) == -1) { 
						fprintf(stderr, "epoll_ctl add connection fd failed\n");
					}
				}
				continue;
			} else {
				socket_fd = events[i].data.fd;
				fprintf(stdout, "get event on fd %d\n", socket_fd);
				while (1) {
					char buf[512];
					if ((n = read(socket_fd, buf, sizeof(buf))) < 0) {
						if (errno != EAGAIN) {
							fprintf(stdout, "errno\n");
							close(socket_fd);	
						}
						break;
					} else if (n == 0) {
						close(socket_fd);
						break;
					} else {
						for (int i = 0; i < n; ++i) {
							buf[i] = rot13_char(buf[i]);
						}
						// buf[n] = "\0";
						if (write(socket_fd, buf, n) < 0) {
							fprintf(stdout, "write error\n");							
						}
					}
				} 
			}
		}
	}
	free(events);
	close(listen_fd);
}



